﻿using Daq.Tools.HttpClientHelper;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Tools
{
    /// <summary>
    /// 文件监控帮助类
    /// </summary>
    public class FileMonitor
    {
        /// <summary>
        /// 文件目录监控对象
        /// </summary>
        private FileSystemWatcher watcher;

        /// <summary>
        /// 文件改变触发
        /// </summary>
        private Action<string> FileChangedEvent = null;

        /// <summary>
        /// 文件路径集合
        /// </summary>
        private ConcurrentDictionary<string, int> fileFullPathList = new ConcurrentDictionary<string, int>();

        /// <summary>
        /// 函数防抖500毫秒
        /// </summary>
        private Debounce debounce = new Debounce(TimeSpan.FromMilliseconds(500));

        /// <summary>
        /// 开始监控文件目录
        /// 1、监控文件内容的修改
        /// 2、监控文件名被修改
        /// 3、监控新文件
        /// </summary>
        /// <param name="catalogue">监视的文件夹或文件</param>
        /// <param name="fileChangedPath">文件改变委托，文件改变的路径</param>
        /// <param name="filter">设置用于确定在目录中监视哪些文件的筛选器字符串。默认值 * 为 "*"。 (监视所有文件。 )</param>
        public void Start(string catalogue, Action<string> fileChangedPath, string filter = "*")
        {
            FileChangedEvent = fileChangedPath;

            watcher = new FileSystemWatcher(catalogue);

            watcher.NotifyFilter = NotifyFilters.Attributes
                                 | NotifyFilters.CreationTime
                                 | NotifyFilters.DirectoryName
                                 | NotifyFilters.FileName
                                 | NotifyFilters.LastAccess
                                 | NotifyFilters.LastWrite
                                 | NotifyFilters.Size;

            watcher.Changed += OnChanged;
            watcher.Created += OnCreated;
            watcher.Deleted += OnDeleted;
            watcher.Renamed += OnRenamed;
            watcher.Error += OnError;

            watcher.Filter = filter; // "*";
            watcher.IncludeSubdirectories = true; // 子目录
            watcher.EnableRaisingEvents = true;
        }

        /// <summary>
        /// 代理文件变化事件
        /// </summary>
        /// <param name="fileFullPath"></param>
        /// <returns></returns>
        private void AgentFileChangedEvent(string fileFullPath)
        {
            fileFullPathList.TryAdd(fileFullPath, 0);

            debounce.Execute(() =>
            {
                for (int i = 0; i < fileFullPathList.Count; i++)
                {
                    var filePath = fileFullPathList.ElementAt(i).Key;

                    try
                    {
                        var fileExtension = Path.GetExtension(filePath);
                        var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
                        if (fileExtension.IsNotNullOrEmpty() && File.Exists(filePath))
                        {
                            var isHidden = (File.GetAttributes(filePath) & FileAttributes.Hidden) == FileAttributes.Hidden; // 是影藏文件
                            if (!isHidden)
                            {
                                var fileSize = new FileInfo(filePath).Length;
                                if (fileSize != 0)
                                {
                                    Task.Run(() =>
                                    {
                                        FileChangedEvent(filePath);
                                    });
                                }
                            }
                        }

                    }
                    catch // 文件可能被占用
                    { }
                    fileFullPathList.TryRemove(filePath, out _);
                    --i;
                }
            });
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        public void Stop()
        {
            if (watcher != null)
            {
                // 停止监控
                watcher.EnableRaisingEvents = false;

                // 释放资源
                watcher.Dispose();
                watcher = null;
            }
        }

        /// <summary>
        /// 文件和目录发生修改
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnChanged(object sender, FileSystemEventArgs e)
        {
            if (e.ChangeType != WatcherChangeTypes.Changed)
            {
                return;
            }
            if (Path.GetExtension(e.FullPath).IsNotNullOrWhiteSpace())
            {
                Console.WriteLine($"\r\n文件发生修改: {e.FullPath}");
            }
            AgentFileChangedEvent(e.FullPath);
        }

        /// <summary>
        /// 创建文件和目录
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnCreated(object sender, FileSystemEventArgs e)
        {
            AgentFileChangedEvent(e.FullPath);

            string value = $"\r\n创建: {e.FullPath}";
            Console.WriteLine(value);
        }

        /// <summary>
        /// 删除文件和目录
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnDeleted(object sender, FileSystemEventArgs e) => Console.WriteLine($"\r\n删除的: {e.FullPath}");

        /// <summary>
        /// 重命名文件和目录
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnRenamed(object sender, RenamedEventArgs e)
        {
            AgentFileChangedEvent(e.FullPath);

            Console.WriteLine($"\r\n改名:");
            Console.WriteLine($"    旧的: {e.OldFullPath}");
            Console.WriteLine($"    新的: {e.FullPath}");
        }

        /// <summary>
        /// 监控异常
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnError(object sender, ErrorEventArgs e) => PrintException(e.GetException());

        private void PrintException(Exception ex)
        {
            if (ex != null)
            {
                Console.WriteLine($"Message: {ex.Message}");
                Console.WriteLine("Stacktrace:");
                Console.WriteLine(ex.StackTrace);
                Console.WriteLine();
                PrintException(ex.InnerException);
            }
        }
    }
}